#ifndef __SENDC_H__
#define __SENDC_H__

#include <string>
#include <map>
using std::string;
using std::map;

namespace sendc {

typedef enum {
    FUNCTION,
    MEMBER
} callable_type;

template<typename Return, typename...Arguments>
class Callable {
    callable_type tp;
public:
    Callable(callable_type _tp) : tp(_tp) {}
    callable_type get_type() { return tp; }
    virtual void operator() (Arguments...) { }
};

template<typename Return, typename...Arguments>
class Function : public Callable<Return, Arguments...> {
    Return (*cb)(Arguments...);
public:
    Function(Return (*_cb)(Arguments...)) : Callable<Return, Arguments...>(FUNCTION), cb(_cb) {}
    
    virtual void operator() (Arguments...argv) { cb(argv...); }
};

template<typename Class, typename Return, typename...Arguments>
class Member : public Callable<Return, Arguments...> {
    Class* c;
    Return (Class::*cb)(Arguments...);
public:
    Member(Class *_c, Return (Class::*_cb)(Arguments...)) : Callable<Return, Arguments...>(MEMBER), c(_c), cb(_cb) {}
    
    virtual void operator() (Arguments...argv) { (c->*cb)(argv...); }
};

class BaseSignal {
public:
    BaseSignal() {}
};

template<typename Return, typename...Arguments>
class Signal : public BaseSignal {
    map<int, Callable<Return, Arguments...>*> callables;
public:
    Signal() {}
    void add_callable(int i, Callable<Return, Arguments...>* call) {
        callables[i] = call;
    }
    
    Return call(int i, Arguments...argv) {
        Callable<Return, Arguments...>* call = callables[i];
        return call->operator()(argv...);
    }
    
    void call_all(Arguments...argv) {
        auto ii = callables.begin();
        for (; ii != callables.end(); ii++)
            call(ii->first, argv...);
    }
    friend class Signals;
};

class Signals {
    map<string, BaseSignal*> signals;
    int id;
public:
    Signals() : id(0) {}
    template<typename Return, typename...Arguments>
    void add_signal(string name) {
        signals[name] = (BaseSignal*) new Signal<Return, Arguments...>();
    }
    
    template<typename Return, typename...Arguments>
    int connect(string name, Return(*cb)(Arguments...)) {
        ((Signal<Return, Arguments...>*)signals[name])->add_callable(++id, new Function<Return, Arguments...>(cb));
        return id;
    }
    template<typename Class, typename Return, typename...Arguments>
    int connect(string name, Class* c, Return(Class::*cb)(Arguments...)) {
        ((Signal<Return, Arguments...>*)signals[name])->add_callable(++id, new Member<Class, Return, Arguments...>(c, cb));
        return id;
    }
    template<typename Return, typename...Arguments>
    void emit(string name, Arguments...argv) {
        ((Signal<Return, Arguments...>*)signals[name])->call_all(argv...);
    }
        
};

}

#endif